/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include <sys/param.h>
#include "athd.h"

/* the following chooses which platform dependent stats
   calculator to run along with the appropriate includes
*/
#if defined(__FreeBSD__)          /* check os freebsd */
#if __FreeBSD_version >= 400000
#define _DO_FREEBSD 1
static apr_status_t _do_freebsd_refresh();
static apr_status_t _do_freebsd_setup();
#define _DO_REFRESH() _do_freebsd_refresh()
#define _DO_SETUP() _do_freebsd_setup()
#endif /* check vers min */

#elif defined(__linux__)                /* check os linux */
#define _DO_LINUX 1
static apr_status_t _do_linux_refresh();
static apr_status_t _do_linux_setup();
#define _DO_REFRESH() _do_linux_refresh();
#define _DO_SETUP() _do_linux_setup();

#elif defined(__sun) && defined(__SVR4) /* check os solaris */
#define _DO_SOLARIS 1
static apr_status_t _do_solaris_refresh();
static apr_status_t _do_solaris_setup();
#define _DO_REFRESH() _do_solaris_refresh()
#define _DO_SETUP() _do_solaris_setup()

#else                                   /* check os fallback */
#define _DO_GENERIC 1
static apr_status_t _do_generic_refresh();
static apr_status_t _do_generic_setup();
#define _DO_REFRESH() _do_generic_refresh()
#define _DO_SETUP() _do_generic_setup()

#endif /* check os dep */

static pthread_mutex_t *_stat_glob_mutex = NULL;
static ath_upd_phys *_stats = NULL;

static void _athd_stats_zero();
static int _athd_stats_glob_lock();
static int _athd_stats_glob_unlock();
static float _athd_stats_get_cus_undef();
static athd_stats_func _athd_stats_get_cus1;
static athd_stats_func _athd_stats_get_cus2;
static athd_stats_func _athd_stats_get_cus3;

apr_status_t
athd_stats_setup(apr_pool_t *pglobal)
{
    apr_status_t rv;
    if (!_stat_glob_mutex)
      {
	  _stat_glob_mutex = apr_pcalloc(pglobal, sizeof(pthread_mutex_t));
	  apr_pool_cleanup_register(pglobal, _stat_glob_mutex,
				    pthread_mutex_cleanup, 
				    apr_pool_cleanup_null);
	  pthread_mutex_init(_stat_glob_mutex, NULL);
      }
    if (!_stats)
      {
	  _stats = ath_phys_upd_alloc(pglobal);
	  _stats->on_u = 0;
	  _stats->up_u = 0;
	  /* always send stats even if empty for now */
	  _stats->cpu_u = 1;
	  _stats->net_u = 1;
	  _stats->ld_u = 1;
	  _stats->mem_u = 1;
	  /* not handled by this app, handled by user's application */
	  _stats->cus1_u = 0;
	  _stats->cus2_u = 0;
	  _stats->cus3_u = 0;
      }
    _athd_stats_zero();

    rv = _DO_SETUP();
    return rv;
}

float
ath_stats_get_by_type(char type)
{
    _athd_stats_glob_lock();
    switch (type)
      {
      case ATH_ALGO_CPU:
	  return _stats->cpu;
	  break;
      case ATH_ALGO_NET:
	  return _stats->net;
	  break;
      case ATH_ALGO_LOD:
	  return _stats->ld;
	  break;
      case ATH_ALGO_MEM:
	  return _stats->mem;
	  break;
      case ATH_ALGO_CU1:
	  return _stats->cus1;
	  break;
      case ATH_ALGO_CU2:
	  return _stats->cus2;
	  break;
      case ATH_ALGO_CU3:
	  return _stats->cus3;
	  break;
      default:
	  return (float)0;
      }
    _athd_stats_glob_unlock();
}

void
athd_stats_set_by_type(char type, float val)
{
    _athd_stats_glob_lock();
    switch (type)
      {
      case ATH_ALGO_CPU:
	  _stats->cpu = val;
	  break;
      case ATH_ALGO_NET:
	  _stats->net = val;
	  break;
      case ATH_ALGO_LOD:
	  _stats->ld = val;
	  break;
      case ATH_ALGO_MEM:
	  _stats->mem = val;
	  break;
      case ATH_ALGO_CU1:
	  _stats->cus1 = val;
	  break;
      case ATH_ALGO_CU2:
	  _stats->cus2 = val;
	  break;
      case ATH_ALGO_CU3:
	  _stats->cus3 = val;
	  break;
      }
    _athd_stats_glob_unlock();
}

void
athd_stats_upd_phys(ath_upd_phys *phys)
{
    if (phys)
      {
	  _athd_stats_glob_lock();
	  ath_phys_upd_clone_stats(phys, _stats);
	  _athd_stats_glob_unlock();
      }
}

apr_status_t
athd_stats_refresh()
{
    apr_status_t rv;
    rv = _DO_REFRESH();
    return rv;
}


/* privates */

static void
_athd_stats_zero()
{
    _athd_stats_glob_lock();
    _stats->cpu = 0;
    _stats->net = 0;
    _stats->ld = 0;
    _stats->mem = 0;
    _stats->cus1 = 0;
    _stats->cus2 = 0;
    _stats->cus3 = 0;
    _athd_stats_glob_unlock();
}    

static int
_athd_stats_glob_lock()
{
    return pthread_mutex_lock(_stat_glob_mutex);
}

static int
_athd_stats_glob_unlock()
{
    return pthread_mutex_unlock(_stat_glob_mutex);
}



/********** FreeBSD stats code **********/
#if defined(_DO_FREEBSD)
#if __FreeBSD_version >= 500101
#include <sys/resource.h>
#else
#include <sys/dkstat.h>
#endif /* vers choose inc */
#include <kvm.h>
#include <nlist.h>
#include <sys/sysctl.h>

static kvm_t *kvm = NULL;
static int use_sysctl_cp_time = 0;
static int sysctl_oid_cp_time[CTL_MAXNAME + 2];
static size_t sysctl_oid_cp_time_len = sizeof(sysctl_oid_cp_time);
const char *sysctl_str_kern_cp_time = "kern.cp_time";

static long _last_cpu_lval = 0;

static apr_status_t _athd_fbsd_kvm_init();
static apr_status_t _athd_fbsd_sysctl_init();
static float _athd_fbsd_cpu_get();
static float _athd_fbsd_net_get();
static float _athd_fbsd_ld_get();
static float _athd_fbsd_mem_get();

static apr_status_t
_do_freebsd_refresh()
{
    apr_status_t rv = APR_SUCCESS;
    _athd_stats_glob_lock();
    _stats->cpu = _athd_fbsd_cpu_get();
    _stats->net = _athd_fbsd_net_get();
    _stats->ld = _athd_fbsd_ld_get();
    _stats->mem = _athd_fbsd_mem_get();
    _athd_stats_glob_unlock();
    return rv;
}

static apr_status_t
_do_freebsd_setup()
{
    apr_status_t rv;
    rv = _athd_fbsd_kvm_init();
    RV_OK = _athd_fbsd_sysctl_init();
    return rv;
}

static apr_status_t
_athd_fbsd_kvm_init()
{
    apr_status_t rv = APR_SUCCESS;
    char kvm_errstr[_POSIX2_LINE_MAX];

    _athd_stats_glob_lock();
    kvm_errstr[0] = (int)NULL;
    if (!kvm)
	kvm = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, kvm_errstr);
    if (!kvm)
      {
	  rv = apr_get_os_error();
	  athd_log_err(ATHD_CRIT, rv, "Cannot open kvm for stats: ");
      }
    _athd_stats_glob_unlock();
    return rv;
}

static apr_status_t
_athd_fbsd_sysctl_init()
{
    int rv;
    int oid_name2oid[2] = { 0, 3 };
    _athd_stats_glob_lock();
    rv = sysctl(oid_name2oid, 2, 
		sysctl_oid_cp_time, 
		&sysctl_oid_cp_time_len, 
		(void *)sysctl_str_kern_cp_time,
		strlen(sysctl_str_kern_cp_time));
    if (!rv)
      {
	  use_sysctl_cp_time = 1;
	  sysctl_oid_cp_time_len /= sizeof(int);
      }
    _athd_stats_glob_unlock();
    return rv;
}

static float
_athd_fbsd_cpu_get()
{
    float fval;
    long lval = 0;
    long cp_time[CPUSTATES];
    int len = sizeof(cp_time);
    long time_busy;

    ASSERT(CLK_TCK);

    if (use_sysctl_cp_time)
	sysctl(sysctl_oid_cp_time, sysctl_oid_cp_time_len,
	       cp_time, &len, 0, 0);
    else
      {
	  struct nlist nl[] = 
	    {
		{ "_cp_time" },
		{ "" }
	    };
      }
    time_busy = cp_time[CP_USER] + cp_time[CP_SYS];
    
    //val = (100 * ((float) time_busy) / ((float) time_denom));
    lval = ((200 * (time_busy / (long)CLK_TCK)) + 1) / 2;

    fval = ((float)lval - (float)_last_cpu_lval);
    _last_cpu_lval = lval;
    //DEBUGOUT("lval: %ld val: %f tick: %ld", lval, val, CLK_TCK);
    //DEBUGOUT("fval: %f ival: %d", fval, lval);
    return fval;
}

static float
_athd_fbsd_net_get()
{
    float val;
    val = 10;
    return val;
}

static float
_athd_fbsd_ld_get()
{
    float val;
    val = 10;
    return val;
}

static float
_athd_fbsd_mem_get()
{
    float val;
    val = 10;
    return val;
}

#endif /* _DO_FREEBSD */



/********** Linux stats code **********/
#if defined(_DO_LINUX)
#include <limits.h>
#include <sys/types.h>
#include <dirent.h>
#include <utmp.h>
#include <paths.h>

static apr_status_t
_do_linux_refresh()
{
    apr_status_t rv = APR_SUCCESS;
    return rv;
}

static apr_status_t
_do_linux_setup()
{
    apr_status_t rv = APR_SUCCESS;
    return rv;
}


#endif /* _DO_LINUX */



/********** Solaris stats code **********/

#if defined(_DO_SOLARIS)
#include <kstat.h>
#include <kvm.h>
#include <fcntl.h>

static apr_status_t
_do_solaris_refresh()
{
    apr_status_t rv = APR_SUCCESS;
    return rv;
}

static apr_status_t
_do_solaris_setup()
{
    apr_status_t rv = APR_SUCCESS;
    return rv;
}

#endif /* _DO_SOLARIS */



/********** Fallback stats code **********/

#if defined(_DO_GENERIC)
static apr_status_t
_do_generic_refresh()
{
    apr_status_t rv = APR_SUCCESS;
    _athd_stats_zero();
    return rv;
}

static apr_status_t
_do_generic_setup()
{
    apr_status_t rv = APR_SUCCESS;
    return rv;
}

#endif /* _DO_GENERIC */

